/*
	
*/
#include <pspkernel.h>
#include <string.h>
/*error*/
#define ERROR(mod,errno) ((1<<31)|(mod<<16)|errno)
#define MOD_G729 0x68
#define ERROR_G729					ERROR(MOD_G729,0)//0x80680000
#define ERROR_G729_BADARG	  ERROR(MOD_G729,1)//0x80680001
#define ERROR_G729_NOTREADY ERROR(MOD_G729,2)//0x80680002
#define ERROR_G729_NOFPL    ERROR(MOD_G729,3)//0x80680003
#define ERROR_G729_TOOLARGE ERROR(MOD_G729,5)//0x80680005
/*macro*/
#define VALID_ADDR(addr) ((((addr+0xA0)|addr)&(0-(k1&0x18)))<0)
#define VALID_ADDR_SIZE(addr,size) (((0-(k1&0x18))&(((((int)addr)+(size))|((int)addr))|(size)))<0)
#define ROUNDUP64(n) ((((int)n)+0x3F)&~0x3F)
/*import*/
extern int sceMeCore_driver_278F2C25(int,...);
extern int sceMeCore_driver_F702E73A(int,...);
/*syslib*/
PSP_MODULE_INFO("sceG729", 0x1006, 0, 0);
int module_start(int argc, char *argv[]){return 0;}
/*.data*/
int G729EncodeHandles=0;//0x1160
int G729EncodeFpl=-1;//0x1164
void*G729EncodeFplAddr=NULL;//0x1168
int G729EncodeFplAddr64=0;//0x116C
int G729DecodeHandles=0;//0x1170
int G729DecodeFpl=-1;//0x1174
void*G729DecodeFplAddr=NULL;//0x1178
int G729DecodeFplAddr64=0;//0x117C
/*.text*/
inline int shiftK1(){
	int ret;
	__asm__ __volatile__ (
	"move %0, $27\n"
	"srl $27, $27, 16\n"
	:"=r"(ret):);
	return ret;
}
inline void setK1(int k1){
	__asm__ __volatile__ (
	"move $27, %0\n"
	::"r"(k1));
}
int sceG729EncodeInitResource(int handles){
	if(handles>=5)return ERROR_G729_TOOLARGE;
	int ret;//,k1=shiftK1();
	if(G729EncodeFplAddr)return ERROR_G729_NOFPL;
	if((G729EncodeFpl=sceKernelCreateFpl("SceLibG729EncodeResource",2,0,((handles * 0x1840)+0x40),1,0))<0)return ERROR_G729;
	if((ret=sceKernelAllocateFpl(G729EncodeFpl,&G729EncodeFplAddr,0))<0){
		sceKernelDeleteFpl(G729EncodeFpl);
		G729EncodeFpl=-1;
		return ret;
	}
	G729EncodeFplAddr64=ROUNDUP64(G729EncodeFplAddr);
	int*addr=(int*)G729EncodeFplAddr64;
	for(int i=0;i<handles;i++,addr+=0x1840)
		addr[16]=0;
	G729EncodeHandles=handles;
	//setK1(k1);
	return 0;
}
int sceG729EncodeTermResource(){
	int ret;//,k1=shiftK1();
	if((!G729EncodeFplAddr)||(!G729EncodeHandles)||(G729EncodeFpl==-1))return ERROR_G729_NOTREADY;
	int*addr=(int*)G729EncodeFplAddr64;
	for(int i=0;i<G729EncodeHandles;i++,addr+=0x1840)
		if(addr[16]==1)return ERROR_G729_BADARG;
	if((ret=sceKernelFreeFpl(G729EncodeFpl,G729EncodeFplAddr))<0)return ret;
	G729EncodeFplAddr64=0;
	G729EncodeFplAddr=0;
	if((ret=sceKernelDeleteFpl(G729EncodeFpl))<0)return ret;
	G729EncodeFpl=-1;
	//setK1(k1);
	return 0;
}
int sceG729EncodeInit(int profile,int unk){
	if((profile<0)||(profile>2)||(unk>1))return ERROR_G729_BADARG;
	if((!G729EncodeFplAddr)||(!G729EncodeHandles)||(G729EncodeFpl==-1))return ERROR_G729_NOTREADY;
	int intr=sceKernelCpuSuspendIntr();
	int handle=0;
	for(int*addr=(int*)G729EncodeFplAddr64;handle<G729EncodeHandles;handle++,addr+=0x1840)
		if(!addr[16])addr[16]=1;
	sceKernelCpuResumeIntr(intr);
	if(handle>=G729EncodeHandles)return ERROR_G729;
	int*var20=(int*)G729EncodeFplAddr64+(handle * 0x1840);
	int*var22=var20+0x40;
	if(var20[16]!=1)return ERROR_G729_NOTREADY;
	var22[1]=profile;
	var22[2]=unk;
	var22[3]=profile?profile!=1?16:10:8;//10=8kbit/s 8=6.4kbit/s
	int k1=shiftK1(),ret=sceMeCore_driver_278F2C25(352,(var20+0x80),profile,unk);
	setK1(k1);
	return ret<0?ret:handle;
}
int sceG729EncodeReset(int handle){
	if((handle<0)||(handle>=G729EncodeHandles))return ERROR_G729_BADARG;
	if((!G729EncodeFplAddr)||(!G729EncodeHandles)||(G729EncodeFpl==-1))return ERROR_G729_NOTREADY;
	int*var7=(int*)G729EncodeFplAddr64+(handle * 0x1840);
	if(var7[16]!=1)return ERROR_G729_NOTREADY;
	int k1=shiftK1(),ret=sceMeCore_driver_278F2C25(352,(var7+0x80),(var7+0x40)[1],(var7+0x40)[2]);
	setK1(k1);
	return ret;
}
int sceG729EncodeExit(int handle){
	if((handle<0)||(handle>=G729EncodeHandles))return ERROR_G729_BADARG;
	if((!G729EncodeFplAddr)||(!G729EncodeHandles)||(G729EncodeFpl==-1))return ERROR_G729_NOTREADY;
	int k1=shiftK1();
	int intr=sceKernelCpuSuspendIntr();
	((int*)(G729EncodeFplAddr64+(handle*0x1840)))[16]=0;
	sceKernelCpuResumeIntr(intr);
	setK1(k1);
	return 0;
}
int sceG729EncodeCore(int handle,int src,int dst){
	if((handle<0)||(handle>=G729EncodeHandles)||(src<1)||(dst<1)||(src&1))return ERROR_G729_BADARG;
	if(!((0x00220202>>((src>>27)&0x1F))&1))return ERROR_G729_BADARG;
	if(!((0x00220202>>((dst>>27)&0x1F))&1))return ERROR_G729_BADARG;
	if((!G729EncodeFplAddr)||(!G729EncodeHandles)||(G729EncodeFpl==-1))return ERROR_G729_NOTREADY;
	int*addr=(int*)G729EncodeFplAddr64+(handle * 0x1840);
	int*var17=addr+0x40;
	if(addr[16]!=1)return ERROR_G729_BADARG;
//	if(VALID_ADDR(src))return 0x80000023;
//	if(VALID_ADDR_SIZE(dst,var17[3]))return 0x80000023;
	sceKernelDcacheWritebackInvalidateRange((void*)src,0xA0);
	sceKernelDcacheInvalidateRange(addr,ROUNDUP64(var17[3]));
	sceKernelDcacheInvalidateRange(addr,ROUNDUP64(var17[3]));
	int k1=shiftK1(),ret=sceMeCore_driver_F702E73A(0x0161,(addr+0x80),src,(0x40000000 |((int)addr&0x1FFFFFFF)));
	memcpy((void*)dst,addr,var17[3]);
	setK1(k1);
	return ret;
}
int sceG729DecodeInitResource(int handles){
	if(handles>=5)return ERROR_G729_TOOLARGE;
//	k1=shiftK1()
	if(G729DecodeFplAddr)return ERROR_G729_NOFPL;
	(G729DecodeFpl=sceKernelCreateFpl("SceLibG729DecodeResource",2,0,((((handles<<2)+handles)<<10)+0x40),1,0));
	if((G729DecodeFpl=sceKernelCreateFpl("SceLibG729DecodeResource",2,0,((((handles<<2)+handles)<<10)+0x40),1,0))<0)return ERROR_G729;
	int ret;
	if((ret=sceKernelAllocateFpl(G729DecodeFpl,&G729DecodeFplAddr,0))<0){
		sceKernelDeleteFpl(G729DecodeFpl);
		G729DecodeFpl=-1;
		return ret;
	}
	G729DecodeFplAddr64=ROUNDUP64(G729DecodeFplAddr);
	for(int i=0,*addr=(int*)G729DecodeFplAddr64;i<handles;i++,addr+=0x1400)
		addr[48]=0;
	G729DecodeHandles=handles;
//	setK1(k1);
	return 0;
}
int sceG729DecodeTermResource(){
//	k1=shiftK1()
	if((!G729DecodeFplAddr)||(!G729DecodeHandles)||(G729DecodeFpl==-1))return ERROR_G729_NOTREADY;
	int ret=0;
	for(int i=0,*addr=(int*)G729DecodeFplAddr64;i<G729DecodeHandles;i++,addr+=0x1400)
		if(addr[48]==1)return ERROR_G729_NOTREADY;
	if(sceKernelFreeFpl(G729DecodeFpl,G729DecodeFplAddr)<0)return ret;
	G729DecodeFplAddr64=0;
	G729DecodeFplAddr=0;
	if((ret=sceKernelDeleteFpl(G729DecodeFpl))<0)return ret;
	G729DecodeFpl=-1;
//	setK1(k1);
	return ret;
}
int sceG729DecodeInit(){
	if((!G729DecodeFplAddr)||(!G729DecodeHandles)||(G729DecodeFpl==-1))return ERROR_G729_NOTREADY;
	int intr=sceKernelCpuSuspendIntr(),handle=0;
	for(int*addr=(int*)G729DecodeFplAddr64;handle<G729DecodeHandles;handle++,addr+=0x1400)
		if(!addr[48])addr[48]=1;
	sceKernelCpuResumeIntr(intr);
	if(handle>=G729DecodeHandles)return ERROR_G729_NOTREADY;
	int*var16=(int*)G729DecodeFplAddr64+(((handle<<2)+handle)<<10);
	if(var16[48]!=1)return ERROR_G729_NOTREADY;
	int k1=shiftK1(),ret=sceMeCore_driver_278F2C25(354,(var16+0x0100));
	setK1(k1);
	return ret;
}
int sceG729DecodeReset(int handle){
	if((handle<0)||(handle>=G729DecodeHandles))return ERROR_G729_BADARG;
	if((!G729DecodeFplAddr)||(!G729DecodeHandles)||(G729DecodeFpl==-1))return ERROR_G729_NOTREADY;
	int*var9=(int*)G729DecodeFplAddr64+(((handle<<2)+handle)<<10);
	if(var9[48]!=1)return ERROR_G729_NOTREADY;
	int k1=shiftK1(),ret=sceMeCore_driver_278F2C25(354,(var9+0x0100),G729DecodeFplAddr,G729DecodeFpl);
	setK1(k1);
	return ret;
}
int sceG729DecodeExit(int handle){
	if((handle<0)||(handle>=G729DecodeHandles))return ERROR_G729_BADARG;
	if((!G729DecodeFplAddr)||(!G729DecodeHandles)||(G729DecodeFpl==-1))return ERROR_G729_NOTREADY;
	int k1=shiftK1(),intr=sceKernelCpuSuspendIntr();
	((int*)(G729DecodeFplAddr64+(((handle<<2)+handle)<<10)))[48]=0;
	sceKernelCpuResumeIntr(intr);
	setK1(k1);
	return 0;
}
int sceG729DecodeCore(int handle,int len,int unk,int src,int*dst){
	if((src<1)||((int)dst<1)||((int)dst&1))return ERROR_G729_BADARG;
	if(!((0x00220202>>(((int)src>>27)&0x1F))&1))return ERROR_G729_BADARG;
	if(!((0x00220202>>(((int)dst>>27)&0x1F))&1))return ERROR_G729_BADARG;
	if((len>0)&&((len^16)>0)){
		if((len^0x10)>0&&(len^0x40)>0)return ERROR_G729_BADARG;
		if((len^0x50)>0&&(len^0x76)>0)return ERROR_G729_BADARG;
	}
	if((handle<0)||(handle>=G729DecodeHandles)||(unk>1))return ERROR_G729_BADARG;
	if((!G729DecodeFplAddr)||(!G729DecodeHandles)||(G729DecodeFpl==-1))return ERROR_G729_NOTREADY;
	int*var18=(int*)G729DecodeFplAddr64+(((handle*4)+handle)<<10);
	if(var18[48]!=1)return ERROR_G729_NOTREADY;
//	if(VALID_ADDR_SIZE(dst,0xA0))return 0x80000023;
//	if(VALID_ADDR_SIZE(src,(len+7)>>3))return 0x80000023;
	sceKernelDcacheWritebackInvalidateRange((void*)src,(len+7)>>3);
	sceKernelDcacheInvalidateRange(var18,0xC0);
	int k1=shiftK1(),ret=sceMeCore_driver_F702E73A(0x0163,(var18+0x0100),len,unk,src,(0x40000000 |((int)var18&0x1FFFFFFF)));
	setK1(k1);
	int*var35=var18+0xA0;
	if((int)((int)var18|(int)dst)&3){//if arg5 or arg5 are not 16 aligned ?
		for(int*var33=var18,*var34=dst;var33!=var35;var33+=0x10,var34+=0x10){
			/*
			a0=LWR(var33,LWL(var33),0);
			a1=LWR(var33,LWL(var33),4);
			var38=LWL(var33,var35,11);
			var35=LWR(var33,var38,8);
			var39=LWL(var33,ret,15);
			ret=LWR(var33,var39,12);
			
			SWR(var34);
			SWL(var34);
			SWR(var34);
			SWL(var34,var35,11);
			SWR(var34,var35,8);
			SWL(var34,ret,15);
			SWR(var34,ret,12);
			*/
		}
	}else{
		for(int*var33=var18,*var34=dst;
		var33!=var35;
		var34+=0x10,var33+=0x10) {
			var34[-0]=var33[0];
			var34[-3]=var33[1];//unchached
			var34[-2]=var33[2];//unchached
			var34[-1]=var33[3];//unchached
		}
	}
	return ret;
}

